#include "base.h"

#include <utility>
#include <math.h>

namespace base {
  Point::Point() { clear(); }
  Point::Point(dtype _x, dtype _y) : x(_x), y(_y) {}
  void Point::clear() { x = y = 0; }
  void Point::reset(dtype _x, dtype _y) { x = _x; y = _y; }
  void Point::offset(dtype dx, dtype dy) { x += dx; y += dy; }

  //-------------------//
  Size::Size() { clear(); }
  Size::Size(dtype _w, dtype _h) : w(_w), h(_h) {}
  void Size::clear() { w = h = 0; }
  void Size::reset(dtype _w, dtype _h) { w = _w; h = _h; }
  void Size::offset(dtype dw, dtype dh) { w += dw; h += dh; }
  
  //-------------------//
  Inset::Inset() { clear(); }
  Inset::Inset(dtype _l, dtype _t, dtype _r, dtype _b)
    : l(_l), t(_t), r(_r), b(_b) {}
  void Inset::clear() { l = t = r = b = 0; }
  void Inset::reset(dtype _l, dtype _t, dtype _r, dtype _b) {
    l = _l; t = _t; r = _r; b = _b;
  }
  void Inset::offset(dtype dl, dtype dt, dtype dr, dtype db) {
    l += dl; t += dt; r += dr; b += db;
  }

  //-------------------//
  Rect::Rect() { clear(); }
  Rect::Rect(dtype _x, dtype _y, dtype _w, dtype _h)
    : x(_x), y(_y), w(_w), h(_h) {}
  void Rect::clear() { x = y = w = h = 0; }
  void Rect::reset(dtype _x, dtype _y, dtype _w, dtype _h) {
    x = _x; y = _y; w = _w; h = _h;
  }
  void Rect::set_boundary(dtype l, dtype t, dtype r, dtype b) {
    x = l; y = t; w = r - l; h = b - t;
  }
  void Rect::set_point(dtype _x, dtype _y) { x = _x; y = _y; }
  void Rect::set_size(dtype _w, dtype _h) { w = _w; h = _h; }
  void Rect::Inset(const struct Inset &inset) {
    Inset(inset.l, inset.t, inset.r, inset.b);
  }
  void Rect::Inset(dtype l, dtype t, dtype r, dtype b) {
    x += l; y += t;
    w = w - l - r;
    h = h - t - b;
  }
  void Rect::offset(dtype dx, dtype dy, dtype dw, dtype dh) {
    x += dx; y += dy; w += dw; h += dh;
  }
  void Rect::offset_point(dtype dx, dtype dy) { x += dx; y += dy; }
  void Rect::offset_size(dtype dw, dtype dh) { w+= dw; h += dh; }
  void Rect::intersect_rect(const Rect &rc) {
    dtype l = base::max(left(), rc.left());
    dtype t = base::max(top(), rc.top());
    dtype r = base::min(right(), rc.right());
    dtype b = base::min(bottom(), rc.bottom());
    set_boundary(l, t, r, b);
  }
  void Rect::union_rect(const Rect &rc) {
    dtype l = base::min(left(), rc.left());
    dtype t = base::min(top(), rc.top());
    dtype r = base::max(right(), rc.right());
    dtype b = base::max(bottom(), rc.bottom());
    set_boundary(l, t, r, b);
  }
  //void Rect::subtract_rect(const Rect &rc) {}

  bool Rect::empty() const { return (w <= 0 || h <= 0); }
  bool Rect::equal(const Rect &rc) const {
    if (x != rc.x) return false;
    if (y != rc.y) return false;
    if (w != rc.w) return false;
    if (h != rc.h) return false;
    return true;
  }
  bool Rect::pt_in_rc(dtype _x, dtype _y, bool include_boundary) const {
    if (include_boundary) {
      if (_x < x || _x > x + w) return false;
      if (_y < y || _y > y + h) return false;
    } else {
      if (_x <= x || _x >= x + w) return false;
      if (_y <= y || _y >= y + h) return false;
    }
    return true;
  }
  bool Rect::is_intersect(const Rect &rc) const {
    dtype l = base::max(left(), rc.left());
    dtype t = base::max(top(), rc.top());
    dtype r = base::min(right(), rc.right());
    dtype b = base::min(bottom(), rc.bottom());
    return (r>l && b>t);
  }

  dtype Rect::left() const { return x; }
  dtype Rect::top() const { return y; }
  dtype Rect::right() const { return x + w; }
  dtype Rect::bottom() const { return y + h; }
  Point Rect::center() const {
    Point pt(x+w/2,y+h/2);
    return std::move(pt);
  }
  Point Rect::left_top() const {
    Point pt(x,y);
    return std::move(pt);
  }
  Point Rect::left_bottom() const {
    Point pt(x,y+h);
    return std::move(pt);
  }
  Point Rect::right_top() const {
    Point pt(x+w,y);
    return std::move(pt);
  }
  Point Rect::right_bottom() const {
    Point pt(x+w,y+h);
    return std::move(pt);
  }
  Size Rect::size() const {
    Size sz(w,h);
    return std::move(sz);
  }

  //-------------------//
  Matrix::Matrix() { clear(); }
  Matrix::Matrix(dtype _a, dtype _b, dtype _c, dtype _d, dtype _e, dtype _f)
    : a(_a), b(_b), c(_c), d(_d), e(_e), f(_f) {}
  // clear to identity
  void Matrix::clear() { a = d = 1; b = c = e = f = 0; }
  void Matrix::reset(dtype _a, dtype _b, dtype _c, dtype _d, dtype _e, dtype _f) {
    a = _a; b = _b; c = _c; d = _d; e = _e; f = _f;
  }
  void Matrix::multipy(const Matrix &matrix) {
    a = a * matrix.a + b * matrix.c;
    b = a * matrix.b + b * matrix.d;
    c = c * matrix.a + d * matrix.c;
    d = c * matrix.b + d * matrix.d;
    e = a * matrix.e + c * matrix.f + e;
    f = b * matrix.e + d * matrix.f + f;
  }
  void Matrix::translate(dtype dx, dtype dy) {
    Matrix tm(
      1.f, 0.f,
      0.f, 1.f,
      dx, dy
    );
    multipy(tm);
  }
  void Matrix::scale(dtype sx, dtype sy) {
    Matrix tm(
      sx, 0.f,
      0.f, sy,
      0.f, 0.f
    );
    multipy(tm);
  }
  void Matrix::rotate(dtype angle) {
    Matrix tm(
      ::cos(a), ::sin(a),
      -::sin(a), ::cos(a),
      0.f, 0.f
    );
    multipy(tm);
  }
  void Matrix::transform(const Point &in, Point &out) {
    out.x = a * in.x + c * in.y + e;
    out.y = b * in.x + d * in.y + f;
  }
  bool Matrix::identity() const {
    if (a != 1.f) return false;
    if (d != 1.f) return false;
    if (b != 0.f) return false;
    if (c != 0.f) return false;
    if (e != 0.f) return false;
    if (f != 0.f) return false;
    return true;
  }
  bool Matrix::equal(const Matrix &m) const {
    if (m.a != a) return false;
    if (m.b != b) return false;
    if (m.c != c) return false;
    if (m.d != d) return false;
    if (m.e != e) return false;
    if (m.f != f) return false;
    return true;
  }

  //-------------------//
  extern bool colormap_find(const char *k, unsigned int &v);
  Color::Color() { clear(); }
  Color::Color(cchar *name) { clear(); reset(name); }
  Color::Color(uint8 _a, uint8 _r, uint8 _g, uint8 _b) { reset(_a, _r, _g, _b); }
  Color::Color(dtype _a, uint8 _r, uint8 _g, uint8 _b) { reset(_a, _r, _g, _b); }
  // clear alpha to 255
  void Color::clear() { a = 255; r = g = b = 0; }
  void Color::reset(cchar *name) {
    unsigned int v;
    if (colormap_find(name, v)) {
      reset((uint8)255, (uint8)(v>>16), (uint8)(v>>8), (uint8)(v>>0));
    }
  }
  void Color::reset(uint8 _a, uint8 _r, uint8 _g, uint8 _b) {
    a = _a; r = _r; g = _g; b = _b;
  }
  void Color::reset(dtype _a, uint8 _r, uint8 _g, uint8 _b) {
    a = (uint8)(_a * 255);
    r = _r; g = _g; b = _b;
  }
} // end namespace base